/*!
 * \file WtUftTicker.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief UFT实时行情处理器实现文件
 * 
 * \details 实现UFT实时行情处理器的所有功能，包括高精度时间控制、
 *          实时行情数据处理、K线周期管理等核心功能。专门针对
 *          超高频交易环境进行了优化。
 */
#include "WtUftTicker.h"
#include "WtUftEngine.h"
#include "../Includes/IDataReader.h"

#include "../Share/TimeUtils.hpp"
#include "../Includes/WTSSessionInfo.hpp"
#include "../Includes/IBaseDataMgr.h"

#include "../WTSTools/WTSLogger.h"

USING_NS_WTP;

/*!
 * \brief 构造函数实现
 * \param engine UFT引擎指针
 * 
 * \details 初始化UFT实时行情处理器，设置引擎关联和初始状态。
 *          所有时间和位置变量都被初始化为合适的默认值。
 */
WtUftRtTicker::WtUftRtTicker(WtUftEngine* engine)
	: _engine(engine)
	, _stopped(false)
	, _date(0)
	, _time(UINT_MAX)
	, _next_check_time(0)
	, _last_emit_pos(0)
	, _cur_pos(0)
{
}

/*!
 * \brief 析构函数实现
 * 
 * \details 清理实时行情处理器资源。具体的清理工作在stop()方法中完成。
 */
WtUftRtTicker::~WtUftRtTicker()
{
}

/*!
 * \brief 初始化行情处理器实现
 * \param sessionID 交易时段ID
 * 
 * \details 根据交易时段ID获取时段信息，并获取当前日期时间作为初始状态。
 *          这是启动行情处理器前的必要准备工作。
 */
void WtUftRtTicker::init(const char* sessionID)
{
	_s_info = _engine->get_session_info(sessionID);

	TimeUtils::getDateTime(_date, _time);
}

/*!
 * \brief 处理实时Tick数据实现
 * \param curTick 当前Tick数据指针
 * 
 * \details 这是核心的Tick数据处理方法，包含复杂的时间控制逻辑：
 *          1. 检查是否有独立处理线程，如果没有则直接转发
 *          2. 验证Tick时间的有效性，过滤过期数据
 *          3. 计算当前分钟位置，处理交易时段边界
 *          4. 管理K线周期的开始和结束
 *          5. 计算下次检查时间，实现精确的时间控制
 */
void WtUftRtTicker::on_tick(WTSTickData* curTick)
{
	if (_thrd == NULL)
	{
		// 如果没有独立线程，直接转发给引擎处理
		if (_engine)
			_engine->on_tick(curTick->code(), curTick);
		return;
	}

	uint32_t uDate = curTick->actiondate();
	uint32_t uTime = curTick->actiontime();

	// 检查时间有效性，过滤过期或乱序的数据
	if (_date != 0 && (uDate < _date || (uDate == _date && uTime < _time)))
	{
		// 当前时间小于本地时间，直接处理但不更新时间状态
		if (_engine)
			_engine->on_tick(curTick->code(), curTick);
		return;
	}

	// 更新当前时间状态
	_date = uDate;
	_time = uTime;

	// 解析时间组件：分钟和秒
	uint32_t curMin = _time / 100000;  // 提取小时分钟部分
	uint32_t curSec = _time % 100000;  // 提取秒和毫秒部分
	uint32_t minutes = _s_info->timeToMinutes(curMin);  // 转换为分钟数
	bool isSecEnd = _s_info->isLastOfSection(curMin);   // 检查是否为时段末尾
	
	// 时段边界处理
	if (isSecEnd)
	{
		minutes--;
	}
	minutes++;
	uint32_t rawMin = curMin;
	curMin = _s_info->minuteToTime(minutes);

	if (_cur_pos == 0)
	{
		// 如果当前位置为0，直接赋值即可
		_cur_pos = minutes;
	}
	else if (_cur_pos < minutes)
	{
		// 如果已记录的分钟小于新的分钟，需要先触发上个周期
		// 这时需要先触发上一个K线周期，再修改平台时间和价格
		if (_last_emit_pos < _cur_pos)
		{
			// 触发数据回报模块
			StdUniqueLock lock(_mtx);

			// 避免重复修改时间戳
			_last_emit_pos = _cur_pos;

			uint32_t thisMin = _s_info->minuteToTime(_cur_pos);

			WTSLogger::info("Minute Bar %u.%04u Closed by data", _date, thisMin);
			_engine->on_minute_end(_date, thisMin);
		}
			
		// 处理当前Tick并更新时间状态
		if (_engine)
		{
			_engine->on_tick(curTick->code(), curTick);
			_engine->set_date_time(_date, curMin, curSec, rawMin);
			_engine->set_trading_date(curTick->tradingdate());
		}

		_cur_pos = minutes;
	}
	else
	{
		// 如果还在同一分钟内，直接处理当前时间和数据
		if (_engine)
		{
			_engine->on_tick(curTick->code(), curTick);
			_engine->set_date_time(_date, curMin, curSec, rawMin);
		}
	}

	// 计算到下一分钟的剩余时间，用于自动触发K线结束
	uint32_t sec = curSec / 1000;      // 秒数
	uint32_t msec = curSec % 1000;     // 毫秒数
	uint32_t left_ticks = (60 - sec) * 1000 - msec;  // 到下一分钟的毫秒数
	_next_check_time = TimeUtils::getLocalTimeNow() + left_ticks;
}

/*!
 * \brief 启动行情处理器实现
 * 
 * \details 启动独立的处理线程，执行以下初始化步骤：
 *          1. 检查是否已有线程运行
 *          2. 触发引擎初始化
 *          3. 计算并设置交易日期
 *          4. 触发交易时段开始事件
 *          5. 创建独立线程进行周期性检查
 * 
 *          处理线程的主要功能：
 *          - 在交易时间内每10毫秒检查一次K线状态
 *          - 自动触发超时的K线结束事件
 *          - 处理交易日期的自动切换
 *          - 在非交易时间降低检查频率
 */
void WtUftRtTicker::run()
{
	if (_thrd)
		return;

	// 触发引擎初始化
	_engine->on_init();

	// 计算当前交易日期
	uint32_t curTDate = _engine->get_basedata_mgr()->calcTradingDate(_s_info->id(), _engine->get_date(), _engine->get_min_time(), true);
	_engine->set_trading_date(curTDate);

	// 触发交易时段开始
	_engine->on_session_begin();

	// 先检查当前时间，预计算偏移
	uint32_t offTime = _s_info->offsetTime(_engine->get_min_time(), true);

	// 创建独立处理线程
	_thrd.reset(new StdThread([this, offTime](){
		while (!_stopped)
		{
			if (_time != UINT_MAX && _s_info->isInTradingTime(_time / 100000, true))
			{
				// 在交易时间内，高频检查（每10毫秒）
				std::this_thread::sleep_for(std::chrono::milliseconds(10));
				uint64_t now = TimeUtils::getLocalTimeNow();

				// 检查是否到达下次检查时间且有未处理的K线
				if (now >= _next_check_time && _last_emit_pos < _cur_pos)
				{
					// 触发数据回报模块
					StdUniqueLock lock(_mtx);

					// 避免重复修改时间戳
					_last_emit_pos = _cur_pos;

					uint32_t thisMin = _s_info->minuteToTime(_cur_pos);
					_time = thisMin;

					// 如果thisMin为0，说明跨夜了
					// 如果是自动K线时间到的话，说明确实跨夜了，要自动+1
					// 同时因为时间变成235959xxx，这里也要手动改为0
					if (thisMin == 0)
					{
						uint32_t lastDate = _date;
						_date = TimeUtils::getNextDate(_date);
						_time = 0;
						WTSLogger::info("Data automatically changed at time 00:00: %u -> %u", lastDate, _date);
					}

					WTSLogger::info("Minute bar %u.%04u closed automatically", _date, thisMin);
					
					// 触发K线结束事件
					_engine->on_minute_end(_date, thisMin);

					// 检查是否到达收盘时间
					uint32_t offMin = _s_info->offsetTime(thisMin, true);
					if (offMin >= _s_info->getCloseTime(true))
					{
						_engine->on_session_end();
					}

					// 更新引擎时间状态（时间格式：145959000）
					if (_engine)
						_engine->set_date_time(_date, thisMin, 0);
				}
			}
			else
			{
				// 不在交易时间，降低检查频率（每10秒）
				// 因为这个逻辑是从数据源驱动的，所以非交易时间低频检查即可，避免过度消耗CPU
				std::this_thread::sleep_for(std::chrono::seconds(10));
			}
		}
	}));
}

/*!
 * \brief 停止行情处理器实现
 * 
 * \details 设置停止标志并等待处理线程安全退出。
 *          使用join()确保线程完全结束后再返回。
 */
void WtUftRtTicker::stop()
{
	_stopped = true;
	if (_thrd)
		_thrd->join();
}
